﻿using System;
using System.Collections;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using biz.ritter.javapi.io;
using biz.ritter.javapi.net;
using java = biz.ritter.javapi;

namespace biz.ritter.javapi.lang
{
    /// <summary>
    /// Summary description for ClassLoader
    /// </summary>
    [Serializable]
    public class ClassLoader
    {
        private AppDomain appDomain;
        private RemoteLoader remoteLoader;
        private Assembly delegateInstance;

        public ClassLoader(URL[] urls)
        {
            foreach (URL url in urls)
            {
                delegateInstance = Assembly.LoadFile(url.ToString());
                break;
            }
        }

        public ClassLoader(string assemblyFile)
        {
            delegateInstance = Assembly.LoadFrom(assemblyFile);
        }

		void SetAppDomainPolicy(AppDomain appDomain)
		{
			// Create an AppDomain policy level.
			PolicyLevel pLevel = PolicyLevel.CreateAppDomainLevel();
			// The root code group of the policy level combines all
			// permissions of its children.
			UnionCodeGroup rootCodeGroup;
			PermissionSet ps = new PermissionSet(PermissionState.None);
			ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
			rootCodeGroup = new UnionCodeGroup(new AllMembershipCondition(), 
				new PolicyStatement(ps, PolicyStatementAttribute.Nothing));

			NamedPermissionSet localIntranet = FindNamedPermissionSet("LocalIntranet");
			// The following code limits all code on this machine to local intranet permissions
			// when running in this application domain.
			UnionCodeGroup virtualIntranet = new UnionCodeGroup(
				new ZoneMembershipCondition(SecurityZone.MyComputer), 
				new PolicyStatement(localIntranet, 
				PolicyStatementAttribute.Nothing));
			virtualIntranet.Name = "Virtual Intranet";
			// Add the code groups to the policy level.
			rootCodeGroup.AddChild(virtualIntranet);
			pLevel.RootCodeGroup = rootCodeGroup;
			appDomain.SetAppDomainPolicy(pLevel);
		}

		private NamedPermissionSet FindNamedPermissionSet(string name)
		{
			IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy();

			while (policyEnumerator.MoveNext())
			{
				PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current;

				if (currentLevel.Label == "Machine")
				{
					IList namedPermissions = currentLevel.NamedPermissionSets;
					IEnumerator namedPermission = namedPermissions.GetEnumerator();

					while (namedPermission.MoveNext())
					{
						if (((NamedPermissionSet)namedPermission.Current).Name == name)
						{
							return ((NamedPermissionSet)namedPermission.Current);
						}
					}
				}
			}
			return null;
		}

		public FunctionList LoadAssembly(string filename)
		{
			return remoteLoader.LoadAssembly(filename);
		}

		public void Unload()
		{
			AppDomain.Unload(appDomain);
			appDomain = null;
		}

        #region methods
        /// <summary>
        /// Sets the default assertion status for this class loader to <tt>false</tt> and discards any package defaults or class assertion status settings associated with the class loader.
        /// </summary>
        public void clearAssertionStatus()
        {
        }

        /// <summary>
        /// <B>Deprecated.</B> <I>Replaced by <A HREF="../../java/lang/ClassLoader.html#defineClass(java.lang.String, byte[], int, int)"><CODE>defineClass(String, byte[], int, int)</CODE></A></I>
        /// </summary>
        protected Class defineClass(sbyte[] b, int off, int len)
        {
            return default(Class);
        }

        /// <summary>
        /// Converts an array of bytes into an instance of class <tt>Class</tt>.
        /// </summary>
        protected Class defineClass(string name, sbyte[] b, int off, int len)
        {
            return default(Class);
        }


        /// <summary>
        /// Finds the specified class.
        /// </summary>
        protected Class findClass(string name)
        {
            return default(Class);
        }

        /// <summary>
        /// Returns the absolute path name of a native library.
        /// </summary>
        protected string findLibrary(string libname)
        {
            return default(string);
        }

        /// <summary>
        /// Returns the class with the given name if this loader has been recorded by the Java virtual machine as an initiating loader of a class with that name.
        /// </summary>
        protected Class findLoadedClass(string name)
        {
            return default(Class);
        }

        /// <summary>
        /// Finds the resource with the given name.
        /// </summary>
        protected Uri findResource(string name)
        {
            return new Uri(name);
        }

        /// <summary>
        /// Finds a class with the specified name, loading it if necessary.
        /// </summary>
        protected Class findSystemClass(string name)
        {
            return default(Class);
        }


        /// <summary>
        /// Returns the parent class loader for delegation.
        /// </summary>
        public ClassLoader getParent()
        {
            return default(ClassLoader);
        }

        /// <summary>
        /// Finds the resource with the given name.
        /// </summary>
        public Uri getResource(string name)
        {
            return new Uri(name);
        }

        /// <summary>
        /// Returns an input stream for reading the specified resource.
        /// </summary>
        public InputStream getResourceAsStream(string name)
        {
            return default(InputStream);
        }

        /// <summary>
        /// Returns the system class loader for delegation.
        /// </summary>
        public static ClassLoader getSystemClassLoader()
        {
            return default(ClassLoader);
        }

        /// <summary>
        /// Find a resource of the specified name from the search path used to load classes.
        /// </summary>
        public static Uri getSystemResource(string name)
        {
            return new Uri(name);
        }

        /// <summary>
        /// Open for reading, a resource of the specified name from the search path used to load classes.
        /// </summary>
        public static InputStream getSystemResourceAsStream(string name)
        {
            return default(InputStream);
        }

        /// <summary>
        /// Loads the class with the specified name.
        /// </summary>
        public Class loadClass(string name)
        {
            return new Class(Type.GetType(name));
        }

        /// <summary>
        /// Loads the class with the specified name.
        /// </summary>
        protected Class loadClass(string name, bool resolve)
        {
            return new Class(Type.GetType(name));
        }

        /// <summary>
        /// Links the specified class.
        /// </summary>
        protected void resolveClass(Class c)
        {
        }

        /// <summary>
        /// Sets the desired assertion status for the named top-level class in this class loader and any nested classes contained therein.
        /// </summary>
        public void setClassAssertionStatus(string className, bool enabled)
        {
        }

        /// <summary>
        /// Sets the default assertion status for this class loader.
        /// </summary>
        public void setDefaultAssertionStatus(bool enabled)
        {
        }

        /// <summary>
        /// Sets the package default assertion status for the named package.
        /// </summary>
        public void setPackageAssertionStatus(string packageName, bool enabled)
        {
        }

        /// <summary>
        /// Sets the signers of a class.
        /// </summary>
        protected void setSigners(Class c, object[] signers)
        {
        }

        #endregion

        public System.Collections.Generic.IEnumerable<Type> GetExportedTypes()
        {
            return this.delegateInstance.GetExportedTypes();
        }
    }
}
